package com.liran.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.util.NumberUtil;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.read.listener.PageReadListener;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.liran.config.APIException;
import com.liran.entity.ChinaAreaEnum;
import com.liran.entity.OriginalExcelData;
import com.liran.entity.OutsideAreaEnum;
import com.liran.entity.TargetExcelData;
import com.liran.mapper.ChinaAreaReflectionMapper;
import com.liran.mapper.OutsideAreaReflectionMapper;
import com.liran.mapper.SourcePlatformReflectionMapper;
import com.liran.mpPojo.ChinaAreaReflection;
import com.liran.mpPojo.OutsideAreaReflection;
import com.liran.mpPojo.SourcePlatformReflection;
import com.liran.param.SourcePlatformReflectionParam;
import com.liran.service.ExcelFileService;
import com.liran.service.ExcelMergeHandler;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URLEncoder;
import java.time.LocalDate;
import java.time.temporal.ChronoField;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author: whh
 * @Date: 2024/4/22
 * @Description:
 */
@Service
@Slf4j
public class ExcelFileServiceImpl implements ExcelFileService {

    @Resource
    private SourcePlatformReflectionMapper sourcePlatformReflectionMapper;

    @Resource
    private ChinaAreaReflectionMapper chinaAreaReflectionMapper;

    @Resource
    private OutsideAreaReflectionMapper outsideAreaReflectionMapper;

    @Override
    public void generateExcelByFile(MultipartFile file, HttpServletResponse response) throws IOException {
        Map<SourcePlatformReflection, Integer> ChinaSourceNumMap = new HashMap<>();
        Map<ChinaAreaReflection, Integer> ChinaAreaNumMap = new HashMap<>();

        Map<SourcePlatformReflection, Integer> OutsideSourceNumMap = new HashMap<>();
        Map<OutsideAreaReflection, Integer> OutsideAreaNumMap = new HashMap<>();
        Map<String, Integer> OutsideCountryNumMap = new HashMap<>();

        List<String> clueTimeList = new ArrayList<>();


        // 这里默认每次会读取100条数据 然后返回过来 直接调用使用数据就行
        // 具体需要返回多少行可以在`PageReadListener`的构造函数设置
        EasyExcel.read(file.getInputStream(), OriginalExcelData.class, new PageReadListener<OriginalExcelData>(dataList -> {

            checkSourceAndTarget(dataList);
            checkArea(dataList);
            for (OriginalExcelData originalExcelData : dataList) {
                if (!clueTimeList.contains(originalExcelData.getClueTime())) {
                    clueTimeList.add(originalExcelData.getClueTime());
                }
                String source = originalExcelData.getSource();
                String platform = originalExcelData.getPlatform();
                String country = originalExcelData.getCountry();
                SourcePlatformReflection sourcePlatformReflection = getByOriginalSourceAndTarget(source, platform);
                String ownerDepartment = originalExcelData.getOwnerDepartment();
                if (country.equals("中国")) {
                    ChinaSourceNumMap.put(sourcePlatformReflection, ChinaSourceNumMap.containsKey(sourcePlatformReflection) ? ChinaSourceNumMap.get(sourcePlatformReflection) + 1 : 1);
                    ChinaAreaReflection chinaAreaReflection = getByChinaOriginalArea(ownerDepartment);
                    ChinaAreaNumMap.put(chinaAreaReflection, ChinaAreaNumMap.containsKey(chinaAreaReflection) ? ChinaAreaNumMap.get(chinaAreaReflection) + 1 : 1);
                } else {
                    OutsideSourceNumMap.put(sourcePlatformReflection, OutsideSourceNumMap.containsKey(sourcePlatformReflection) ? OutsideSourceNumMap.get(sourcePlatformReflection) + 1 : 1);
                    OutsideAreaReflection outsideAreaReflection = getByOutsideOriginalArea(ownerDepartment);
                    OutsideAreaNumMap.put(outsideAreaReflection, OutsideAreaNumMap.containsKey(outsideAreaReflection) ? OutsideAreaNumMap.get(outsideAreaReflection) + 1 : 1);
                    OutsideCountryNumMap.put(country, OutsideCountryNumMap.containsKey(country) ? OutsideCountryNumMap.get(country) + 1 : 1);
                }
            }
        }, 200)).sheet().doRead();

        initSourceEnum(ChinaSourceNumMap);
        initSourceEnum(OutsideSourceNumMap);
        initChinaAreaMap(ChinaAreaNumMap);
        initOutSideAreaMap(OutsideAreaNumMap);

        String clueTime = generateClueTime(clueTimeList);
        int max = NumberUtil.max(ChinaSourceNumMap.size(), OutsideSourceNumMap.size(), ChinaAreaNumMap.size(), OutsideAreaNumMap.size(), OutsideCountryNumMap.size());
        List<TargetExcelData> targetExcelDataList = new ArrayList<>(max);
        for (int i = 0; i < max; i++) {
            targetExcelDataList.add(i, TargetExcelData.builder().insideDate(clueTime).outsideDate(clueTime).build());
        }
        //插入国内来源数据
        List<SourcePlatformReflection> ChinaSourceNumMapKeyList = new ArrayList<>(ChinaSourceNumMap.keySet());
        ChinaSourceNumMapKeyList.sort(Comparator.comparingInt(SourcePlatformReflection::getId));
        for (int i = 0; i < ChinaSourceNumMapKeyList.size(); i++) {
            targetExcelDataList.get(i).setInsideSource(ChinaSourceNumMapKeyList.get(i).getTargetSource());
            targetExcelDataList.get(i).setInsidePlatform(ChinaSourceNumMapKeyList.get(i).getTargetPlatform());
            targetExcelDataList.get(i).setInsideSourceNum(ChinaSourceNumMap.get(ChinaSourceNumMapKeyList.get(i)));
        }
        //插入国内区域数据
        List<ChinaAreaReflection> ChinaCountryList = new ArrayList<>(ChinaAreaNumMap.keySet());
        ChinaCountryList.sort(Comparator.comparingInt(ChinaAreaReflection::getId));
        for (int i = 0; i < ChinaCountryList.size(); i++) {
            ChinaAreaReflection chinaAreaReflection = ChinaCountryList.get(i);
            targetExcelDataList.get(i).setInsideArea(chinaAreaReflection.getTargetArea());
            targetExcelDataList.get(i).setInsideAreaNum(ChinaAreaNumMap.get(chinaAreaReflection));
        }
        //插入国外来源数据
        List<SourcePlatformReflection> OutsideSourceNumMapKeyList = new ArrayList<>(OutsideSourceNumMap.keySet());
        OutsideSourceNumMapKeyList.sort(Comparator.comparingInt(SourcePlatformReflection::getId));
        for (int i = 0; i < OutsideSourceNumMapKeyList.size(); i++) {
            targetExcelDataList.get(i).setOutsideSource(OutsideSourceNumMapKeyList.get(i).getTargetSource());
            targetExcelDataList.get(i).setOutsidePlatform(OutsideSourceNumMapKeyList.get(i).getTargetPlatform());
            targetExcelDataList.get(i).setOutsideSourceNum(OutsideSourceNumMap.get(OutsideSourceNumMapKeyList.get(i)));
        }
        //插入国外国家数据
        List<String> OutsideCountryNumMapKeyList = new ArrayList<>(OutsideCountryNumMap.keySet());
        for (int i = 0; i < OutsideCountryNumMapKeyList.size(); i++) {
            String outsideCountry = OutsideCountryNumMapKeyList.get(i);
            targetExcelDataList.get(i).setOutsideCountry(outsideCountry);
            targetExcelDataList.get(i).setOutsideCountryNum(OutsideCountryNumMap.get(outsideCountry));
        }
        //插入国外区域数据
        List<OutsideAreaReflection> OutsideAreaNumMapKeyList = new ArrayList<>(OutsideAreaNumMap.keySet());
        OutsideAreaNumMapKeyList.sort(Comparator.comparingInt(OutsideAreaReflection::getId));
        for (int i = 0; i < OutsideAreaNumMapKeyList.size(); i++) {
            OutsideAreaReflection outsideAreaReflection = OutsideAreaNumMapKeyList.get(i);
            targetExcelDataList.get(i).setOutsideArea(outsideAreaReflection.getTargetArea());
            targetExcelDataList.get(i).setOutsideAreaNum(OutsideAreaNumMap.get(outsideAreaReflection));
        }
        int[] needMergeColumnIndexs = new int[]{0, 1, 7, 8};
        ExcelMergeHandler insideLoopMergeStrategy = new ExcelMergeHandler(0, max + 1, needMergeColumnIndexs);
        ExcelMergeHandler outsideLoopMergeStrategy = new ExcelMergeHandler(0, max + 1, needMergeColumnIndexs);

        //
        //String targetFileName = FILE_PREFIX + "taget" + System.currentTimeMillis() + ".xlsx";
        //// 这里 需要指定写用哪个class去写，然后写到第一个sheet，名字为模板 然后文件流会自动关闭
        //// 如果这里想使用03 则 传入excelType参数即可
        //EasyExcel.write(targetFileName, TargetExcelData.class)
        //        .registerWriteHandler(insideLoopMergeStrategy)
        //        .registerWriteHandler(outsideLoopMergeStrategy)
        //        .sheet("模板")
        //        .doWrite(targetExcelDataList);
        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
        response.setCharacterEncoding("utf-8");
        // 这里URLEncoder.encode可以防止中文乱码 当然和easyexcel没有关系
        String fileName = URLEncoder.encode("销售线索" + clueTime, "UTF-8").replaceAll("\\+", "%20");
        response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");
        // 这里需要设置不关闭流
        EasyExcel.write(response.getOutputStream(), TargetExcelData.class)
                .registerWriteHandler(insideLoopMergeStrategy)
                .registerWriteHandler(outsideLoopMergeStrategy)
                .autoCloseStream(Boolean.FALSE)
                .sheet("模板")
                .doWrite(targetExcelDataList);

    }

    @Override
    public Boolean config(SourcePlatformReflectionParam param) {
        SourcePlatformReflection sourcePlatformReflection = new SourcePlatformReflection();
        BeanUtil.copyProperties(param, sourcePlatformReflection);
        if (getByOriginalSourceAndTarget(param.getOriginalSource(), param.getOriginalPlatform()) != null) {
            throw new APIException("来源与平台已存在映射关系");
        }
        int insert = sourcePlatformReflectionMapper.insert(sourcePlatformReflection);
        return insert == 1;
    }

    @Override
    public Boolean chinaAreaConfig(ChinaAreaReflection chinaAreaReflection) {
        if(getByChinaOriginalArea(chinaAreaReflection.getOriginalArea()) != null){
            throw new APIException("已存在中国区域配置");
        }
        int insert = chinaAreaReflectionMapper.insert(chinaAreaReflection);
        return insert == 1;
    }

    @Override
    public Boolean outsideAreaConfig(OutsideAreaReflection outsideAreaReflection) {
        if(getByOutsideOriginalArea(outsideAreaReflection.getOriginalArea()) != null){
            throw new APIException("已存在国外区域配置");
        }
        int insert = outsideAreaReflectionMapper.insert(outsideAreaReflection);
        return insert == 1;
    }

    private void checkArea(List<OriginalExcelData> originalExcelDataList) {
        List<String> errorAreaUserList = new ArrayList<>();
        for (OriginalExcelData originalExcelData : originalExcelDataList) {
            if ("中国".equals(originalExcelData.getCountry())) {
                ChinaAreaReflection chinaAreaReflection = getByChinaOriginalArea(originalExcelData.getOwnerDepartment());
                if (chinaAreaReflection == null) {
                    errorAreaUserList.add(originalExcelData.getName());
                }
            } else {
                OutsideAreaReflection outsideAreaReflection = getByOutsideOriginalArea(originalExcelData.getOwnerDepartment());
                if (outsideAreaReflection == null) {
                    errorAreaUserList.add(originalExcelData.getName());
                }
            }
        }
        if (!errorAreaUserList.isEmpty()) {
            throw new APIException(String.format("用户[%s]负责人主属部门信息有误,请查验!", errorAreaUserList));
        }
    }

    private SourcePlatformReflection getByOriginalSourceAndTarget(String originalSource, String originalPlatform) {
        QueryWrapper<SourcePlatformReflection> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("original_source", originalSource);
        queryWrapper.eq("original_platform", originalPlatform);
        return sourcePlatformReflectionMapper.selectOne(queryWrapper);
    }

    private OutsideAreaReflection getByOutsideOriginalArea(String ownerDepartment) {
        QueryWrapper<OutsideAreaReflection> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("original_area", ownerDepartment);
        return outsideAreaReflectionMapper.selectOne(queryWrapper);
    }

    private ChinaAreaReflection getByChinaOriginalArea(String ownerDepartment) {
        QueryWrapper<ChinaAreaReflection> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("original_area", ownerDepartment);
        return chinaAreaReflectionMapper.selectOne(queryWrapper);
    }

    private void checkSourceAndTarget(List<OriginalExcelData> originalExcelDataList) {
        List<String> errorSourceUserList = new ArrayList<>();
        for (OriginalExcelData originalExcelData : originalExcelDataList) {
            SourcePlatformReflection sourcePlatformReflection = getByOriginalSourceAndTarget(originalExcelData.getSource(), originalExcelData.getPlatform());
            if (sourcePlatformReflection == null) {
                errorSourceUserList.add(originalExcelData.getName());
            }
        }
        if (!errorSourceUserList.isEmpty()) {
            throw new APIException(String.format("用户[%s]来源与平台映射有误,请查验!", errorSourceUserList));
        }
    }


    private String generateClueTime(List<String> clueTimeList) {
        List<LocalDate> dateTimeList = clueTimeList.stream().map(s -> LocalDateTimeUtil.parseDate(s, "yyyy-MM-dd")).collect(Collectors.toList());
        if (dateTimeList.size() == 1) {
            return dateTimeList.get(0).getMonth().getValue() + "." + dateTimeList.get(0).getDayOfMonth();
        } else {
            dateTimeList.sort(Comparator.comparingInt(date -> Integer.parseInt(String.valueOf(date.get(ChronoField.DAY_OF_YEAR)))));
            LocalDate beginLocalDate = dateTimeList.get(0);
            LocalDate endLocalDate = dateTimeList.get(dateTimeList.size() - 1);
            return beginLocalDate.getMonth().getValue() + "." + beginLocalDate.getDayOfMonth() + "-" + endLocalDate.getMonth().getValue() + "." + endLocalDate.getDayOfMonth();
        }

    }

    private void initSourceEnum(Map<SourcePlatformReflection, Integer> chinaSourceNumMap) {
        Integer sum = 0;
        for (Integer value : chinaSourceNumMap.values()) {
            sum += value;
        }
        chinaSourceNumMap.put(SourcePlatformReflection.builder().targetPlatform("总计").id(Integer.MAX_VALUE).build(), sum);
    }

    private void initChinaAreaMap(Map<ChinaAreaReflection, Integer> areaNumMap) {
        List<ChinaAreaReflection> chinaAreaReflections = chinaAreaReflectionMapper.selectList(new QueryWrapper<>());
        for (ChinaAreaReflection chinaAreaReflection : chinaAreaReflections) {
            if (!areaNumMap.containsKey(chinaAreaReflection)) {
                areaNumMap.put(chinaAreaReflection, 0);
            }
        }
    }

    private void initOutSideAreaMap(Map<OutsideAreaReflection, Integer> OutsideAreaNumMap) {
        List<OutsideAreaReflection> outsideAreaReflections = outsideAreaReflectionMapper.selectList(new QueryWrapper<>());
        for (OutsideAreaReflection outsideAreaReflection : outsideAreaReflections) {
            if (!OutsideAreaNumMap.containsKey(outsideAreaReflection)) {
                OutsideAreaNumMap.put(outsideAreaReflection, 0);
            }
        }
    }

}
